General Setup

Setup chunk

Setup reticulate

knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/pallium_evo"

Load libraries

library(reticulate)
knitr::knit_engines$set(python = reticulate::eng_python)
py_available(initialize = FALSE)
[1] FALSE
use_python(Sys.which("python"))
py_config()
python:         /home/tpires/bin/miniconda3/bin/python
libpython:      /home/tpires/bin/miniconda3/lib/libpython3.8.so
pythonhome:     /home/tpires/bin/miniconda3:/home/tpires/bin/miniconda3
version:        3.8.3 (default, May 19 2020, 18:47:26)  [GCC 7.3.0]
numpy:          /home/tpires/bin/miniconda3/lib/python3.8/site-packages/numpy
numpy_version:  1.18.5

NOTE: Python version was forced by RETICULATE_PYTHON

Prepare data for cell2location

Load spatial data

library(Seurat)

Check data

visD = readRDS("/links/groups/treutlein/USERS/Ashley/projects/axolotl/experiments/20210201_axolotl_brain_52/data_processing/D1_113_sub_b_processed.RDS")

Save for reading into python

SpatialDimPlot(visD, group.by = "seurat_clusters")

SpatialFeaturePlot(visD, features = "nCount_Spatial")
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

Load cell2location results

Load results

test = read.csv("results/cell2loc/predictions_cell2loc.csv", header = T, row.names = 1)
visD = AddMetaData(visD, test)

Plot all individual scores

test = read.csv("results/cell2loc/predictions_cell2loc.csv", header = T, row.names = 1)
visD = AddMetaData(visD, test)

Combine scores

plotSpatialScores = function(obj, ct_use, img_name, colours = rainbow(length(ct_use))){
  names(colours) = ct_use
  
  # setup plotting data
  plot_df = obj@meta.data[,ct_use]
  plot_df = data.frame(apply(plot_df, 2, scales::rescale, to = c(0,1)))
  #for(n in ct_use) plot_df[,n][plot_df[,n]<.5] = 0
  coordinates = Seurat::GetTissueCoordinates(obj@images[[img_name]])
  data = cbind(coordinates, plot_df[rownames(x = coordinates),])
  
  plot = ggplot(data = data, aes_string(x = colnames(x = data)[2], 
                                        y = colnames(x = data)[1]))+
    Seurat:::geom_spatial(data = data, mapping = aes_string(alpha = ct_use[1]), point.size.factor = 1.6, 
                          image = obj@images[[img_name]], image.alpha = 0.5, colour = colours[1], shape = 19,
                          crop = T, stroke = .25)
  for(n in ct_use[-1]){
    plot = plot +
      Seurat:::geom_spatial(data = data, mapping = aes_string(alpha = n), point.size.factor = 1.6, 
                            image = obj@images[[img_name]], image.alpha = 0, colour = colours[n], shape = 19,
                            crop = T, stroke = .25)
  }
  
  plot = plot +
      labs(alpha = "Normalised score")+
      theme_classic()+
      theme(aspect.ratio = 1,
            axis.title = element_blank(),
            axis.ticks = element_blank(),
            axis.line = element_blank(),
            axis.text = element_blank())
  return(plot)
}

plotSpatialScores = function(obj, ct_use, img_name, groups = ct_use, colours = rainbow(length(ct_use))){
  names(colours) = ct_use
  
  # setup plotting data
  plot_df = obj@meta.data[,ct_use]
  plot_df = data.frame(apply(plot_df, 2, scales::rescale, to = c(0,1)))
  # for each group, get the top value
  if(!all(groups==ct_use)){
    for(g in names(groups)){
      plot_df[,g] = apply(plot_df[,groups[[g]]], 1, max)
    }
  } else{
    groups = lapply(ct_use, function(x) x)
    names(groups) = ct_use
  }
  plot_df$max_g = apply(plot_df[,names(groups)], 1, function(x) names(groups)[which.max(x)])
  rownames(plot_df) = rownames(obj@meta.data)
  
  coordinates = Seurat::GetTissueCoordinates(obj@images[[img_name]])
  data = cbind(coordinates, plot_df[rownames(x = coordinates),])
  
  plot = ggplot(data = data, aes_string(x = colnames(x = data)[2], 
                                        y = colnames(x = data)[1]))
  for(n in names(groups)){
    plot = plot +
      Seurat:::geom_spatial(data = data[data$max_g==n,], mapping = aes_string(alpha = n), point.size.factor = 2, 
                            image = obj@images[[img_name]], image.alpha = ifelse(n==names(groups)[1], 0.5, 0), 
                            colour = colours[n], shape = 19, crop = T, stroke = .25)
  }
  
  plot = plot +
      labs(alpha = "Normalised score")+
      theme_classic()+
      theme(aspect.ratio = 1,
            axis.title = element_blank(),
            axis.ticks = element_blank(),
            axis.line = element_blank(),
            axis.text = element_blank())
  return(plot)
}

plotSpatialScores = function(obj, ct_use, img_name, groups = ct_use, colours = rainbow(length(ct_use))){
  # setup plotting data
  plot_df = obj@meta.data[,ct_use]
  plot_df = data.frame(apply(plot_df, 2, scales::rescale, to = c(0,1)))
  # for each group, get the top value
  if(!all(groups==ct_use)){
    for(g in names(groups)){
      plot_df[,g] = apply(plot_df[,groups[[g]]], 1, max)
    }
  } else{
    groups = lapply(ct_use, function(x) x)
    names(groups) = ct_use
  }
  plot_df$max_g = apply(plot_df[,names(groups)], 1, function(x) names(groups)[which.max(x)])
  rownames(plot_df) = rownames(obj@meta.data)
  
  coordinates = Seurat::GetTissueCoordinates(obj@images[[img_name]])
  data = cbind(coordinates, plot_df[rownames(x = coordinates),])
  
  plot = ggplot(data = data, aes_string(x = colnames(x = data)[2], 
                                        y = colnames(x = data)[1]))
  plt_list = list()
  for(n in names(groups)){
    plt_list[[n]] = plot +
      Seurat:::geom_spatial(data = data, mapping = aes_string(alpha = n), point.size.factor = 4, 
                            image = obj@images[[img_name]], image.alpha = 0.5, 
                            colour = colours[n], shape = 19, crop = T, stroke = .25)
    plt_list[[n]] = plt_list[[n]] +
      labs(alpha = paste0("Max score\n", n))+
      scale_alpha_continuous(range = c(0,1))+
      theme_classic()+
      theme(aspect.ratio = 1,
            axis.title = element_blank(),
            axis.ticks = element_blank(),
            axis.line = element_blank(),
            axis.text = element_blank())
  }
  return(plt_list)
}



ct_use = c("glut_SUBSET_0", "glut_SUBSET_7", "glut_SUBSET_11", "glut_SUBSET_13",
           "glut_SUBSET_1", "glut_SUBSET_3",
           "glut_SUBSET_2", "glut_SUBSET_10", "glut_SUBSET_22")

reg_cols_simp = c("medial" = "#52168D", "dorsal" = "#C56007", "lateral" = "#118392")
reg_cols_simp = c(rep(reg_cols_simp[1], 4), rep(reg_cols_simp[2], 2), rep(reg_cols_simp[3], 3))

groups = list("medial" = c("glut_SUBSET_0", "glut_SUBSET_7", "glut_SUBSET_11", "glut_SUBSET_13"),
              "dorsal" = c("glut_SUBSET_1", "glut_SUBSET_3"),
              "lateral" = c("glut_SUBSET_2", "glut_SUBSET_10", "glut_SUBSET_22"))
names(reg_cols_simp) = ct_use
xxx = plotSpatialScores(obj = visD, ct_use = ct_use, img_name = "slice2", colours = reg_cols_simp)
cowplot::plot_grid(plotlist = xxx, ncol = 3)
LS0tCnRpdGxlOiAiUGxvdCBtYXBwaW5nIHRvIFZpc2l1bSBkYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMgR2VuZXJhbCBTZXR1cApTZXR1cCBjaHVuawoKYGBge3IsIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gOCkKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBub3JtYWxpemVQYXRoKCIuLiIpKQprbml0cjo6b3B0c19rbml0JGdldCgicm9vdC5kaXIiKQpgYGAKClNldHVwIHJldGljdWxhdGUKCmBgYHtyfQpsaWJyYXJ5KHJldGljdWxhdGUpCmtuaXRyOjprbml0X2VuZ2luZXMkc2V0KHB5dGhvbiA9IHJldGljdWxhdGU6OmVuZ19weXRob24pCnB5X2F2YWlsYWJsZShpbml0aWFsaXplID0gRkFMU0UpCnVzZV9weXRob24oU3lzLndoaWNoKCJweXRob24iKSkKcHlfY29uZmlnKCkKYGBgCgpMb2FkIGxpYnJhcmllcwoKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpgYGAKCgoKIyBQcmVwYXJlIGRhdGEgZm9yIGNlbGwybG9jYXRpb24KTG9hZCBzcGF0aWFsIGRhdGEKCmBgYHtyfQp2aXNEID0gcmVhZFJEUygiL2xpbmtzL2dyb3Vwcy90cmV1dGxlaW4vVVNFUlMvQXNobGV5L3Byb2plY3RzL2F4b2xvdGwvZXhwZXJpbWVudHMvMjAyMTAyMDFfYXhvbG90bF9icmFpbl81Mi9kYXRhX3Byb2Nlc3NpbmcvRDFfMTEzX3N1Yl9iX3Byb2Nlc3NlZC5SRFMiKQpgYGAKCkNoZWNrIGRhdGEKCmBgYHtyfQpTcGF0aWFsRGltUGxvdCh2aXNELCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiKQpTcGF0aWFsRmVhdHVyZVBsb3QodmlzRCwgZmVhdHVyZXMgPSAibkNvdW50X1NwYXRpYWwiKQpgYGAKClNhdmUgZm9yIHJlYWRpbmcgaW50byBweXRob24KCmBgYHtyfQpTZXVyYXREaXNrOjpTYXZlTG9vbSh2aXNELCAiZGF0YS9wcm9jZXNzZWQvRDFfMTEzX3N1Yl9iLmxvb20iKQpgYGAKCgoKIyBMb2FkIGNlbGwybG9jYXRpb24gcmVzdWx0cwpMb2FkIHJlc3VsdHMKCmBgYHtyfQp0ZXN0ID0gcmVhZC5jc3YoInJlc3VsdHMvY2VsbDJsb2MvcHJlZGljdGlvbnNfY2VsbDJsb2MuY3N2IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSkKdmlzRCA9IEFkZE1ldGFEYXRhKHZpc0QsIHRlc3QpCmBgYAoKUGxvdCBhbGwgaW5kaXZpZHVhbCBzY29yZXMKCmBgYHtyfQpwZGYoInJlc3VsdHMvY2VsbDJsb2MvY2VsbDJsb2Nfc2xpY2UxMTNfRDFfc3ViX2IucGRmIiwgaGVpZ2h0ID0gMTAsIHdpZHRoID0gMTApCmZvcihuIGluIGNvbG5hbWVzKHZpc0RAbWV0YS5kYXRhKVsxOToxMTJdKXsKICBwcmludChTcGF0aWFsRmVhdHVyZVBsb3QodmlzRCwgZmVhdHVyZXMgPSBuLCBwdC5zaXplLmZhY3RvciA9IDIuNykpCn0KZGV2Lm9mZigpCmBgYAoKQ29tYmluZSBzY29yZXMKCmBgYHtyfQpwbG90U3BhdGlhbFNjb3JlcyA9IGZ1bmN0aW9uKG9iaiwgY3RfdXNlLCBpbWdfbmFtZSwgY29sb3VycyA9IHJhaW5ib3cobGVuZ3RoKGN0X3VzZSkpKXsKICBuYW1lcyhjb2xvdXJzKSA9IGN0X3VzZQogIAogICMgc2V0dXAgcGxvdHRpbmcgZGF0YQogIHBsb3RfZGYgPSBvYmpAbWV0YS5kYXRhWyxjdF91c2VdCiAgcGxvdF9kZiA9IGRhdGEuZnJhbWUoYXBwbHkocGxvdF9kZiwgMiwgc2NhbGVzOjpyZXNjYWxlLCB0byA9IGMoMCwxKSkpCiAgI2ZvcihuIGluIGN0X3VzZSkgcGxvdF9kZlssbl1bcGxvdF9kZlssbl08LjVdID0gMAogIGNvb3JkaW5hdGVzID0gU2V1cmF0OjpHZXRUaXNzdWVDb29yZGluYXRlcyhvYmpAaW1hZ2VzW1tpbWdfbmFtZV1dKQogIGRhdGEgPSBjYmluZChjb29yZGluYXRlcywgcGxvdF9kZltyb3duYW1lcyh4ID0gY29vcmRpbmF0ZXMpLF0pCiAgCiAgcGxvdCA9IGdncGxvdChkYXRhID0gZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sbmFtZXMoeCA9IGRhdGEpWzJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBjb2xuYW1lcyh4ID0gZGF0YSlbMV0pKSsKICAgIFNldXJhdDo6Omdlb21fc3BhdGlhbChkYXRhID0gZGF0YSwgbWFwcGluZyA9IGFlc19zdHJpbmcoYWxwaGEgPSBjdF91c2VbMV0pLCBwb2ludC5zaXplLmZhY3RvciA9IDEuNiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgaW1hZ2UgPSBvYmpAaW1hZ2VzW1tpbWdfbmFtZV1dLCBpbWFnZS5hbHBoYSA9IDAuNSwgY29sb3VyID0gY29sb3Vyc1sxXSwgc2hhcGUgPSAxOSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjcm9wID0gVCwgc3Ryb2tlID0gLjI1KQogIGZvcihuIGluIGN0X3VzZVstMV0pewogICAgcGxvdCA9IHBsb3QgKwogICAgICBTZXVyYXQ6OjpnZW9tX3NwYXRpYWwoZGF0YSA9IGRhdGEsIG1hcHBpbmcgPSBhZXNfc3RyaW5nKGFscGhhID0gbiksIHBvaW50LnNpemUuZmFjdG9yID0gMS42LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltYWdlID0gb2JqQGltYWdlc1tbaW1nX25hbWVdXSwgaW1hZ2UuYWxwaGEgPSAwLCBjb2xvdXIgPSBjb2xvdXJzW25dLCBzaGFwZSA9IDE5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JvcCA9IFQsIHN0cm9rZSA9IC4yNSkKICB9CiAgCiAgcGxvdCA9IHBsb3QgKwogICAgICBsYWJzKGFscGhhID0gIk5vcm1hbGlzZWQgc2NvcmUiKSsKICAgICAgdGhlbWVfY2xhc3NpYygpKwogICAgICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocGxvdCkKfQoKcGxvdFNwYXRpYWxTY29yZXMgPSBmdW5jdGlvbihvYmosIGN0X3VzZSwgaW1nX25hbWUsIGdyb3VwcyA9IGN0X3VzZSwgY29sb3VycyA9IHJhaW5ib3cobGVuZ3RoKGN0X3VzZSkpKXsKICBuYW1lcyhjb2xvdXJzKSA9IGN0X3VzZQogIAogICMgc2V0dXAgcGxvdHRpbmcgZGF0YQogIHBsb3RfZGYgPSBvYmpAbWV0YS5kYXRhWyxjdF91c2VdCiAgcGxvdF9kZiA9IGRhdGEuZnJhbWUoYXBwbHkocGxvdF9kZiwgMiwgc2NhbGVzOjpyZXNjYWxlLCB0byA9IGMoMCwxKSkpCiAgIyBmb3IgZWFjaCBncm91cCwgZ2V0IHRoZSB0b3AgdmFsdWUKICBpZighYWxsKGdyb3Vwcz09Y3RfdXNlKSl7CiAgICBmb3IoZyBpbiBuYW1lcyhncm91cHMpKXsKICAgICAgcGxvdF9kZlssZ10gPSBhcHBseShwbG90X2RmWyxncm91cHNbW2ddXV0sIDEsIG1heCkKICAgIH0KICB9IGVsc2V7CiAgICBncm91cHMgPSBsYXBwbHkoY3RfdXNlLCBmdW5jdGlvbih4KSB4KQogICAgbmFtZXMoZ3JvdXBzKSA9IGN0X3VzZQogIH0KICBwbG90X2RmJG1heF9nID0gYXBwbHkocGxvdF9kZlssbmFtZXMoZ3JvdXBzKV0sIDEsIGZ1bmN0aW9uKHgpIG5hbWVzKGdyb3Vwcylbd2hpY2gubWF4KHgpXSkKICByb3duYW1lcyhwbG90X2RmKSA9IHJvd25hbWVzKG9iakBtZXRhLmRhdGEpCiAgCiAgY29vcmRpbmF0ZXMgPSBTZXVyYXQ6OkdldFRpc3N1ZUNvb3JkaW5hdGVzKG9iakBpbWFnZXNbW2ltZ19uYW1lXV0pCiAgZGF0YSA9IGNiaW5kKGNvb3JkaW5hdGVzLCBwbG90X2RmW3Jvd25hbWVzKHggPSBjb29yZGluYXRlcyksXSkKICAKICBwbG90ID0gZ2dwbG90KGRhdGEgPSBkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2xuYW1lcyh4ID0gZGF0YSlbMl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGNvbG5hbWVzKHggPSBkYXRhKVsxXSkpCiAgZm9yKG4gaW4gbmFtZXMoZ3JvdXBzKSl7CiAgICBwbG90ID0gcGxvdCArCiAgICAgIFNldXJhdDo6Omdlb21fc3BhdGlhbChkYXRhID0gZGF0YVtkYXRhJG1heF9nPT1uLF0sIG1hcHBpbmcgPSBhZXNfc3RyaW5nKGFscGhhID0gbiksIHBvaW50LnNpemUuZmFjdG9yID0gMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbWFnZSA9IG9iakBpbWFnZXNbW2ltZ19uYW1lXV0sIGltYWdlLmFscGhhID0gaWZlbHNlKG49PW5hbWVzKGdyb3VwcylbMV0sIDAuNSwgMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gY29sb3Vyc1tuXSwgc2hhcGUgPSAxOSwgY3JvcCA9IFQsIHN0cm9rZSA9IC4yNSkKICB9CiAgCiAgcGxvdCA9IHBsb3QgKwogICAgICBsYWJzKGFscGhhID0gIk5vcm1hbGlzZWQgc2NvcmUiKSsKICAgICAgdGhlbWVfY2xhc3NpYygpKwogICAgICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocGxvdCkKfQoKcGxvdFNwYXRpYWxTY29yZXMgPSBmdW5jdGlvbihvYmosIGN0X3VzZSwgaW1nX25hbWUsIGdyb3VwcyA9IGN0X3VzZSwgY29sb3VycyA9IHJhaW5ib3cobGVuZ3RoKGN0X3VzZSkpKXsKICAjIHNldHVwIHBsb3R0aW5nIGRhdGEKICBwbG90X2RmID0gb2JqQG1ldGEuZGF0YVssY3RfdXNlXQogIHBsb3RfZGYgPSBkYXRhLmZyYW1lKGFwcGx5KHBsb3RfZGYsIDIsIHNjYWxlczo6cmVzY2FsZSwgdG8gPSBjKDAsMSkpKQogICMgZm9yIGVhY2ggZ3JvdXAsIGdldCB0aGUgdG9wIHZhbHVlCiAgaWYoIWFsbChncm91cHM9PWN0X3VzZSkpewogICAgZm9yKGcgaW4gbmFtZXMoZ3JvdXBzKSl7CiAgICAgIHBsb3RfZGZbLGddID0gYXBwbHkocGxvdF9kZlssZ3JvdXBzW1tnXV1dLCAxLCBtYXgpCiAgICB9CiAgfSBlbHNlewogICAgZ3JvdXBzID0gbGFwcGx5KGN0X3VzZSwgZnVuY3Rpb24oeCkgeCkKICAgIG5hbWVzKGdyb3VwcykgPSBjdF91c2UKICB9CiAgcGxvdF9kZiRtYXhfZyA9IGFwcGx5KHBsb3RfZGZbLG5hbWVzKGdyb3VwcyldLCAxLCBmdW5jdGlvbih4KSBuYW1lcyhncm91cHMpW3doaWNoLm1heCh4KV0pCiAgcm93bmFtZXMocGxvdF9kZikgPSByb3duYW1lcyhvYmpAbWV0YS5kYXRhKQogIAogIGNvb3JkaW5hdGVzID0gU2V1cmF0OjpHZXRUaXNzdWVDb29yZGluYXRlcyhvYmpAaW1hZ2VzW1tpbWdfbmFtZV1dKQogIGRhdGEgPSBjYmluZChjb29yZGluYXRlcywgcGxvdF9kZltyb3duYW1lcyh4ID0gY29vcmRpbmF0ZXMpLF0pCiAgCiAgcGxvdCA9IGdncGxvdChkYXRhID0gZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sbmFtZXMoeCA9IGRhdGEpWzJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBjb2xuYW1lcyh4ID0gZGF0YSlbMV0pKQogIHBsdF9saXN0ID0gbGlzdCgpCiAgZm9yKG4gaW4gbmFtZXMoZ3JvdXBzKSl7CiAgICBwbHRfbGlzdFtbbl1dID0gcGxvdCArCiAgICAgIFNldXJhdDo6Omdlb21fc3BhdGlhbChkYXRhID0gZGF0YSwgbWFwcGluZyA9IGFlc19zdHJpbmcoYWxwaGEgPSBuKSwgcG9pbnQuc2l6ZS5mYWN0b3IgPSA0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltYWdlID0gb2JqQGltYWdlc1tbaW1nX25hbWVdXSwgaW1hZ2UuYWxwaGEgPSAwLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gY29sb3Vyc1tuXSwgc2hhcGUgPSAxOSwgY3JvcCA9IFQsIHN0cm9rZSA9IC4yNSkKICAgIHBsdF9saXN0W1tuXV0gPSBwbHRfbGlzdFtbbl1dICsKICAgICAgbGFicyhhbHBoYSA9IHBhc3RlMCgiTWF4IHNjb3JlXG4iLCBuKSkrCiAgICAgIHNjYWxlX2FscGhhX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsMSkpKwogICAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpKQogIH0KICByZXR1cm4ocGx0X2xpc3QpCn0KCgoKY3RfdXNlID0gYygiZ2x1dF9TVUJTRVRfMCIsICJnbHV0X1NVQlNFVF83IiwgImdsdXRfU1VCU0VUXzExIiwgImdsdXRfU1VCU0VUXzEzIiwKICAgICAgICAgICAiZ2x1dF9TVUJTRVRfMSIsICJnbHV0X1NVQlNFVF8zIiwKICAgICAgICAgICAiZ2x1dF9TVUJTRVRfMiIsICJnbHV0X1NVQlNFVF8xMCIsICJnbHV0X1NVQlNFVF8yMiIpCgpyZWdfY29sc19zaW1wID0gYygibWVkaWFsIiA9ICIjNTIxNjhEIiwgImRvcnNhbCIgPSAiI0M1NjAwNyIsICJsYXRlcmFsIiA9ICIjMTE4MzkyIikKcmVnX2NvbHNfc2ltcCA9IGMocmVwKHJlZ19jb2xzX3NpbXBbMV0sIDQpLCByZXAocmVnX2NvbHNfc2ltcFsyXSwgMiksIHJlcChyZWdfY29sc19zaW1wWzNdLCAzKSkKCmdyb3VwcyA9IGxpc3QoIm1lZGlhbCIgPSBjKCJnbHV0X1NVQlNFVF8wIiwgImdsdXRfU1VCU0VUXzciLCAiZ2x1dF9TVUJTRVRfMTEiLCAiZ2x1dF9TVUJTRVRfMTMiKSwKICAgICAgICAgICAgICAiZG9yc2FsIiA9IGMoImdsdXRfU1VCU0VUXzEiLCAiZ2x1dF9TVUJTRVRfMyIpLAogICAgICAgICAgICAgICJsYXRlcmFsIiA9IGMoImdsdXRfU1VCU0VUXzIiLCAiZ2x1dF9TVUJTRVRfMTAiLCAiZ2x1dF9TVUJTRVRfMjIiKSkKbmFtZXMocmVnX2NvbHNfc2ltcCkgPSBjdF91c2UKeHh4ID0gcGxvdFNwYXRpYWxTY29yZXMob2JqID0gdmlzRCwgY3RfdXNlID0gY3RfdXNlLCBpbWdfbmFtZSA9ICJzbGljZTIiLCBjb2xvdXJzID0gcmVnX2NvbHNfc2ltcCkKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0geHh4LCBuY29sID0gMykKYGBgCgoKCgoKCgo=